package com.liuyc.lib.common.mvvm.view

import android.os.Bundle
import com.liuyc.lib.common.base.BaseFragmentDialog
import com.liuyc.lib.common.mvvm.vm.BaseViewModel
import kotlin.reflect.KClass
import org.koin.androidx.viewmodel.ext.android.getStateViewModel
import org.koin.androidx.viewmodel.ext.android.getViewModel

/**
 * ViewBaseDialogFragment
 *基于 MVVM 设计中 view 层 dialogFragment
 * 刘隽
 */
abstract class ViewBaseDialogFragment<VM : BaseViewModel>(private val layoutId: Int)
    : BaseFragmentDialog(layoutId) {

    lateinit var mViewModel: VM

    /**
     *
     */
    override fun initViewBefore() {
        initViewModelBefore()
        initViewModel();
    }

    /**
     * 初始化 viewModel
     * 由于 使用 koin 注入 所以在这边直接使用 koin 的getViewModel 方法
     * 该方法与  Any(ps：必须是明确类型) by inject 不同 getViewModel 会从 底层activity 或者Fragment
     * 去获取上一次的 viewModel
     */
    fun initViewModel() {
        //获取 父类的第一个泛型类型
        //等同于 java的  Class<T> tClass = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        val clazz =
            this.javaClass.kotlin.supertypes[0].arguments[0].type!!.classifier!! as KClass<VM>


        var bundle: Bundle? = loadViewModelBundle()
        // 由于在父类获取无法准确知道 vm 的具体类型，所以无法示使用 by inject 进行懒加载
        // 另外也不建议使用 by inject 进行对 vm 的初始化
        // inject 初始化 会将整个 vm 的生命周期交付给 koinScope 管理 并不与 android 底层挂钩，
        // 如果 vm 并非使用 single 的话 会导致 视图在重构的时候 vm 会是一个全新的值
        // 而使用 getStateViewModel 获取的话 会在视图重构的时候将上一次 的vm 回传过来
        if (bundle == null) mViewModel = getViewModel(clazz)
        else mViewModel = getStateViewModel(clazz)
    }


    /**
     * 加载 vm 需要重构使用的 bundle 参数
     * 以下情况在 数据重构需要注意：
     * 如果不重写该参数则会直接通过 koin 管理方案生成 VM
     * 如果写了，则会在 视图 重构的时候 直接获取 在第一次根据 创建的时候 根据 koin 生成
     * 接下来的重构视图 则会使用 androidx 底层传递回来的对象
     */
    fun loadViewModelBundle(): Bundle? {
        return null
    }

    /**
     * 在执行 initViewModel 之前的操作
     * 如果需要使用 initViewModel 之后的操作
     * 直接重写 initViewBefore 在super 之后便是
     */
    abstract fun initViewModelBefore()
}